package com.itaming.lycheeframework.support.cipher.symmetric;

import com.itaming.lycheeframework.support.cipher.AbstractEncryptor;
import com.itaming.lycheeframework.support.cipher.CipherAlgorithm;
import com.itaming.lycheeframework.support.utils.CharPool;
import com.itaming.lycheeframework.support.utils.ObjectUtil;
import com.itaming.lycheeframework.support.utils.StringPool;
import com.itaming.lycheeframework.support.utils.StringUtil;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;

/**
 * 对称加解密器抽象类
 *
 * @author A.Ming
 */
public abstract class AbstractSymmetricEncryptor extends AbstractEncryptor<SymmetricEncryptorOptions> {

    /**
     * 获取初始化向量长度，单位：bit
     *
     * @return 初始化向量长度
     */
    protected abstract int getIvBitLength();

    /**
     * 获取SecretKey
     *
     * @param key          密钥
     * @param keyBitLength 密钥长度(bit)
     * @return SecretKey
     */
    protected abstract SecretKey getSecretKey(String key, int keyBitLength) throws Exception;

    /**
     * 加密
     *
     * @param data    待加密数据
     * @param options 加密器选项
     * @return 加密后的数据
     */
    @Override
    public byte[] encrypt(byte[] data, SymmetricEncryptorOptions options) {
        checkOptions(options);
        return doFinal(Cipher.ENCRYPT_MODE, data, options);
    }

    /**
     * 解密
     *
     * @param data    待解密数据
     * @param options 加密器选项
     * @return 解密后的数据
     */
    @Override
    public byte[] decrypt(byte[] data, SymmetricEncryptorOptions options) {
        checkOptions(options);
        return doFinal(Cipher.DECRYPT_MODE, data, options);
    }

    /**
     * 初始化Cipher实例
     *
     * @param instance   Cipher实例
     * @param cipherMode 加解密模式，加密：{@link Cipher#ENCRYPT_MODE}，解密：{@link Cipher#DECRYPT_MODE}
     * @param options    加密器选项
     * @throws Exception
     */
    @Override
    protected void initCipherInstance(Cipher instance, int cipherMode, SymmetricEncryptorOptions options) throws Exception {
        CipherAlgorithm cipherAlgorithm = options.cipherAlgorithm();
        SecretKey secretKeyObj = getSecretKey(options.secretKey(), cipherAlgorithm.getKeyBitLength());
        if (cipherAlgorithm.isEcb()) {
            instance.init(cipherMode, secretKeyObj);
        } else if (cipherAlgorithm.isCbc()) {
            byte[] ivBytes = getKeyBytes(ObjectUtil.defaultIfNull(options.iv(), StringPool.EMPTY), getIvBitLength());
            IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
            instance.init(cipherMode, secretKeyObj, ivParameterSpec);
        }
    }

    /**
     * 获取密钥字节数组，根据密钥长度截取或补齐密钥
     *
     * @param key           密钥
     * @param keyLengthBits 目标密钥长度(bit)
     * @return 处理后的密钥数据
     */
    protected byte[] getKeyBytes(String key, int keyLengthBits) {
        key = ObjectUtil.defaultIfNull(key, StringPool.EMPTY);

        // 截取或补齐到指定长度
        int keyLength = key.length();
        int needLength = keyLengthBits / 8;

        if (keyLength > needLength) {
            key = key.substring(0, needLength);
        } else if (keyLength < needLength) {
            key = StringUtil.rightPad(key, needLength, CharPool.NULL0);
        }

        return StringUtil.getBytes(key);
    }

}
